<script>
	
	let activeFn;

	function isObject(obj) {
		return obj !== null && !Array.isArray(obj) && typeof obj === 'object';
	}


  // 存储代理对象，避免重复代理
	let proxyMap = new WeakMap();

  // 存储最外层的依赖地图
  let depsMap = new WeakMap();

  function track(target, key) {
    // 如果当前没有激活的副作用函数，则不需要收集依赖
    if(!activeFn) return;
    // 从地图中取出该对象所有属性的依赖情况
    let targetDeps = depsMap.get(target);  
    if(!targetDeps) {
      targetDeps = new Map();
      depsMap.set(target, targetDeps);
    }
    // 从对象找到该属性的依赖情况
    let objKeyDeps = targetDeps.get(key);
    if(!objKeyDeps) {
      objKeyDeps = new Set();
      targetDeps.set(key, objKeyDeps);
    }
    // 添加副作用函数的
    objKeyDeps.add(activeFn);
  }

  function trigger(target, key) {
    // 从地图中取出该对象所有属性的依赖情况
    let targetDeps = depsMap.get(target);  
    if(!targetDeps) return;
    // 从对象找到该属性的依赖情况
    let objKeyDeps = targetDeps.get(key);
    if(!objKeyDeps) return;
    // 派发更新依次执行所有依赖的副作用函数
    objKeyDeps.forEach(fn=>fn());
  }
 
	// 数据劫持
	function reactive(obj) {
		if (!isObject(obj)) {
			return obj;
		}
		// 为了提高效率缓存起来，避免重复代理
		let result = proxyMap.get(obj); // 以当前对象作为键，看看该对象是否被代理过
    if (result) {
      return result;
    }
		result = new Proxy(obj, {
			get(target, key) {
				let value = Reflect.get(target, key);
				value = reactive(value);
				// 收集依赖
				track(target, key);
				console.log('get ' + key + ':', value);
				return value;
			},
			set(target, key, value) {
				// 如果是对象，递归劫持
				value = reactive(value);
				console.log('set ' + key + ':', value);
				Reflect.set(target, key, value);
        trigger(target, key);
			}
		});
		// 没有被代理过，就缓存起来
		proxyMap.set(obj, result);
    return result;
	}

	function autoRun(fn) {
		function updateWrapper() {
			activeFn = updateWrapper;
			fn(); // 该函数的运行期间，activeUpdate一定有值
			activeFn = null;
		}
		updateWrapper();
	}

  let state = reactive({
		name: 'moniaca',
		age: 18,
		province: {
			city: {
				name: '北京'
			}
		}
	});

	autoRun(() => {
		console.log('依赖的副作用函数执行了', state.age);
	});

	// state.age = 20;
</script>
